package xuan.core.daliuren;

import java.util.*;

import xuan.core.daliuren.settings.DaLiuRenShenShaSetting;
import xuan.utils.DateUtil;
import xuan.utils.CommonUtil;
import com.nlf.calendar.Lunar;
import com.nlf.calendar.Solar;
import xuan.core.daliuren.maps.DaLiuRenJiChuMap;
import xuan.core.daliuren.utils.DaLiuRenJiChuUtil;
import xuan.core.daliuren.settings.DaLiuRenJiChuSetting;

/**
 * 大六壬
 *
 * @author 善待
 */
public class DaLiuRen {

    /**
     * 大六壬 - 基础设置
     */
    private DaLiuRenJiChuSetting daLiuRenJiChuSetting;
    /**
     * 大六壬 - 神煞设置
     */
    private DaLiuRenShenShaSetting daLiuRenShenShaSetting;

    /**
     * 公历日期
     */
    private Solar solar;
    /**
     * 农历日期
     */
    private Lunar lunar;

    /**
     * 年干
     */
    private String yearGan;
    /**
     * 月干
     */
    private String monthGan;
    /**
     * 日干
     */
    private String dayGan;
    /**
     * 时干
     */
    private String hourGan;

    /**
     * 年支
     */
    private String yearZhi;
    /**
     * 月支
     */
    private String monthZhi;
    /**
     * 日支
     */
    private String dayZhi;
    /**
     * 时支
     */
    private String hourZhi;

    /**
     * 年干支
     */
    private String yearGanZhi;
    /**
     * 月干支
     */
    private String monthGanZhi;
    /**
     * 日干支
     */
    private String dayGanZhi;
    /**
     * 时干支
     */
    private String hourGanZhi;

    /**
     * 天干（1~12宫）
     */
    private List<String> tianGan;
    /**
     * 地盘（1~12宫）
     */
    private List<String> diPan;
    /**
     * 天盘（1~12宫）
     */
    private List<String> tianPan;
    /**
     * 神盘（1~12宫）
     */
    private List<String> shenPan;

    /**
     * 四课
     */
    private List<List<String>> siKe;

    /**
     * 三传
     */
    private List<String> sanChuan;
    /**
     * 三传取法
     */
    private String sanChuanQuFa;

//*******************************************************************************************************************************

    /**
     * 使用基础设置初始化
     *
     * @param daLiuRenJiChuSetting 大六壬 - 基础设置
     */
    public DaLiuRen(DaLiuRenJiChuSetting daLiuRenJiChuSetting) {

        this.daLiuRenJiChuSetting = daLiuRenJiChuSetting; // 基础设置

        // 1、获取日期
        Map<String, Object> date = DaLiuRenJiChuUtil.getDate(daLiuRenJiChuSetting);
        this.solar = (Solar) date.get("solar"); // 公历日期
        this.lunar = (Lunar) date.get("lunar"); // 农历日期

        // 2、获取干支
        Map<String, List<String>> ganZhi = DaLiuRenJiChuUtil.getGanZhi(daLiuRenJiChuSetting, this.lunar);
        List<String> yearGanZhi = ganZhi.get("yearGanZhi");
        this.yearGan = yearGanZhi.get(0); // 年干
        this.yearZhi = yearGanZhi.get(1); // 年支
        this.yearGanZhi = yearGanZhi.get(2); // 年干支
        List<String> monthGanZhi = ganZhi.get("monthGanZhi");
        this.monthGan = monthGanZhi.get(0); // 月干
        this.monthZhi = monthGanZhi.get(1); // 月支
        this.monthGanZhi = monthGanZhi.get(2); // 月干支
        List<String> dayGanZhi = ganZhi.get("dayGanZhi");
        this.dayGan = dayGanZhi.get(0); // 日干
        this.dayZhi = dayGanZhi.get(1); // 日支
        this.dayGanZhi = dayGanZhi.get(2); // 日干支
        List<String> hourGanZhi = ganZhi.get("hourGanZhi");
        this.hourGan = hourGanZhi.get(0); // 时干
        this.hourZhi = hourGanZhi.get(1); // 时支
        this.hourGanZhi = hourGanZhi.get(2); // 时干支

        // 3、初始化必要数据
        initializeDiPan(); // 初始化地盘
        initializeTianPan(); // 初始化天盘
        initializeTianGan(); // 初始化天干
        initializeShenPan(); // 初始化神盘
        initializeSiKe(); // 初始化四课
        initializeSanChuan(); // 初始化三传

    }

    /**
     * 神煞设置（可选）
     * <p>
     * <hr/>
     * 可选，若不设置则使用默认值，详情请在设置中查看
     */
    public void daLiuRenShenShaSetting(DaLiuRenShenShaSetting daLiuRenShenShaSetting) {
        this.daLiuRenShenShaSetting = daLiuRenShenShaSetting;
    }

//===============================================================================================================================

    /**
     * 获取公历日期（Solar型）
     *
     * @return 公历日期（如：2024-01-01）
     */
    public Solar getSolar() {
        return this.solar;
    }

    /**
     * 获取农历日期（Lunar型）
     *
     * @return 农历日期（如：二〇二三年冬月二十）
     */
    public Lunar getLunar() {
        return this.lunar;
    }

    /**
     * 获取公历日期（String型）
     *
     * @return 公历日期（如：2024年01月01日00时00分00秒）
     */
    public String getSolarStr() {
        return DateUtil.getSolarStr(this.solar);
    }

    /**
     * 获取农历日期（String型）
     *
     * @return 农历日期（如：二〇二三年冬月二十(早)子时）
     */
    public String getLunarStr() {
        return DateUtil.getLunarStr(this.lunar);
    }

    /**
     * 获取公历日期（Date型）
     *
     * @return 公历日期（如：Mon Jan 01 00:00:00 CST 2024）
     */
    public Date getSolarDate() {
        return DateUtil.stringToDate(DateUtil.getSolarStr(this.solar));
    }

    /**
     * 获取农历日期（Date型）
     *
     * @return 农历日期（如：Mon Nov 20 00:00:00 CST 2023）
     */
    public Date getLunarDate() {
        return DateUtil.stringToDate(DateUtil.getLunarStr2(this.lunar));
    }


    /**
     * 获取姓名
     *
     * @return 姓名（如：缘主）
     */
    public String getName() {
        return this.daLiuRenJiChuSetting.getName();
    }

    /**
     * 获取性别
     *
     * @return 性别（如：男）
     */
    public String getSex() {
        return this.daLiuRenJiChuSetting.getSex() == 0 ? "女" : "男";
    }

    /**
     * 获取占事
     *
     * @return 占事（如：未填写）
     */
    public String getOccupy() {
        return this.daLiuRenJiChuSetting.getOccupy();
    }

    /**
     * 获取造
     *
     * @return 造（如：乾造）
     */
    public String getZao() {
        return this.daLiuRenJiChuSetting.getSex() == 0 ? "坤造" : "乾造";
    }

    /**
     * 获取太岁类型
     *
     * @return 太岁类型，若无数据则返回null（如：值）
     */
    public String getTaiSuiType() {

        if (this.daLiuRenJiChuSetting.getYearGanZhiType() == 0) {
            return DaLiuRenJiChuMap.TAI_SUI_TYPE.get(new Lunar().getYearZhi() + this.yearZhi); // 以正月初一起算
        } else if (this.daLiuRenJiChuSetting.getYearGanZhiType() == 1) {
            DaLiuRenJiChuMap.TAI_SUI_TYPE.get(new Lunar().getYearZhiByLiChun() + this.yearZhi); // 以立春当天起算
        } else if (this.daLiuRenJiChuSetting.getYearGanZhiType() == 2) {
            DaLiuRenJiChuMap.TAI_SUI_TYPE.get(new Lunar().getYearZhiExact() + this.yearZhi); // 以立春交接时刻起算
        }

        return DaLiuRenJiChuMap.TAI_SUI_TYPE.get(new Lunar().getYearZhi() + this.yearZhi);

    }

    /**
     * 获取星期
     *
     * @return 星期（如：周一）
     */
    public String getXingQi() {
        return "周" + this.lunar.getWeekInChinese();
    }

    /**
     * 获取季节
     *
     * @return 季节（如：孟春）
     */
    public String getJiJie() {
        return this.lunar.getSeason();
    }

    /**
     * 获取生肖
     *
     * @return 生肖（如：鼠）
     */
    public String getShengXiao() {

        if (this.daLiuRenJiChuSetting.getYearGanZhiType() == 0) {
            return this.lunar.getYearShengXiao(); // 以正月初一起算
        } else if (this.daLiuRenJiChuSetting.getYearGanZhiType() == 1) {
            return this.lunar.getYearShengXiaoByLiChun(); // 以立春当天起算
        } else if (this.daLiuRenJiChuSetting.getYearGanZhiType() == 2) {
            return this.lunar.getYearShengXiaoExact(); // 以立春交接时刻起算
        }

        return this.lunar.getYearShengXiao();

    }

    /**
     * 获取星座
     *
     * @return 星座（如：魔羯座）
     */
    public String getXingZuo() {
        return this.solar.getXingZuo() + "座";
    }

    /**
     * 获取五不遇时
     *
     * @return 五不遇时（true:符合。false:不符合）
     */
    public boolean getWuBuYuShi() {
        return this.hourGanZhi.equals(DaLiuRenJiChuMap.WU_BU_YU_SHI.get(this.dayGan));
    }

    /**
     * 获取年干
     *
     * @return 年干（如：甲）
     */
    public String getYearGan() {
        return this.yearGan;
    }

    /**
     * 获取月干
     *
     * @return 月干（如：甲）
     */
    public String getMonthGan() {
        return this.monthGan;
    }

    /**
     * 获取日干
     *
     * @return 日干（如：甲）
     */
    public String getDayGan() {
        return this.dayGan;
    }

    /**
     * 获取时干
     *
     * @return 时干（如：甲）
     */
    public String getHourGan() {
        return this.hourGan;
    }


    /**
     * 获取年支
     *
     * @return 年支（如：子）
     */
    public String getYearZhi() {
        return this.yearZhi;
    }

    /**
     * 获取月支
     *
     * @return 月支（如：子）
     */
    public String getMonthZhi() {
        return this.monthZhi;
    }

    /**
     * 获取日支
     *
     * @return 日支（如：子）
     */
    public String getDayZhi() {
        return this.dayZhi;
    }

    /**
     * 获取时支
     *
     * @return 时支（如：子）
     */
    public String getHourZhi() {
        return this.hourZhi;
    }


    /**
     * 获取年干支
     *
     * @return 年干支（如：甲子）
     */
    public String getYearGanZhi() {
        return this.yearGanZhi;
    }

    /**
     * 获取月干支
     *
     * @return 月干支（如：甲子）
     */
    public String getMonthGanZhi() {
        return this.monthGanZhi;
    }

    /**
     * 获取日干支
     *
     * @return 日干支（如：甲子）
     */
    public String getDayGanZhi() {
        return this.dayGanZhi;
    }

    /**
     * 获取时干支
     *
     * @return 时干支（如：甲子）
     */
    public String getHourGanZhi() {
        return this.hourGanZhi;
    }


    /**
     * 获取年干五行
     *
     * @return 年干五行（如：木）
     */
    public String getYearGanWuXing() {
        return DaLiuRenJiChuMap.TIAN_GAN_WU_XING.get(this.yearGan);
    }

    /**
     * 获取月干五行
     *
     * @return 月干五行（如：木）
     */
    public String getMonthGanWuXing() {
        return DaLiuRenJiChuMap.TIAN_GAN_WU_XING.get(this.monthGan);
    }

    /**
     * 获取日干五行
     *
     * @return 日干五行（如：木）
     */
    public String getDayGanWuXing() {
        return DaLiuRenJiChuMap.TIAN_GAN_WU_XING.get(this.dayGan);
    }

    /**
     * 获取时干五行
     *
     * @return 时干五行（如：木）
     */
    public String getHourGanWuXing() {
        return DaLiuRenJiChuMap.TIAN_GAN_WU_XING.get(this.hourGan);
    }


    /**
     * 获取年支五行
     *
     * @return 年支五行（如：木）
     */
    public String getYearZhiWuXing() {
        return DaLiuRenJiChuMap.DI_ZHI_WU_XING.get(this.yearZhi);
    }

    /**
     * 获取月支五行
     *
     * @return 月支五行（如：木）
     */
    public String getMonthZhiWuXing() {
        return DaLiuRenJiChuMap.DI_ZHI_WU_XING.get(this.monthZhi);
    }

    /**
     * 获取日支五行
     *
     * @return 日支五行（如：木）
     */
    public String getDayZhiWuXing() {
        return DaLiuRenJiChuMap.DI_ZHI_WU_XING.get(this.dayZhi);
    }

    /**
     * 获取时支五行
     *
     * @return 时支五行（如：木）
     */
    public String getHourZhiWuXing() {
        return DaLiuRenJiChuMap.DI_ZHI_WU_XING.get(this.hourZhi);
    }


    /**
     * 获取年干支五行
     *
     * @return 年干支五行（如：木水）
     */
    public String getYearGanZhiWuXing() {
        return getYearGanWuXing() + getYearZhiWuXing();
    }

    /**
     * 获取月干支五行
     *
     * @return 月干支五行（如：木水）
     */
    public String getMonthGanZhiWuXing() {
        return getMonthGanWuXing() + getMonthZhiWuXing();
    }

    /**
     * 获取日干支五行
     *
     * @return 日干支五行（如：木水）
     */
    public String getDayGanZhiWuXing() {
        return getDayGanWuXing() + getDayZhiWuXing();
    }

    /**
     * 获取时干支五行
     *
     * @return 时干支五行（如：木水）
     */
    public String getHourGanZhiWuXing() {
        return getHourGanWuXing() + getHourZhiWuXing();
    }


    /**
     * 获取年干支纳音
     *
     * @return 年干支纳音（如：天上火）
     */
    public String getYearGanZhiNaYin() {
        return DaLiuRenJiChuMap.NA_YIN.get(this.yearGanZhi);
    }

    /**
     * 获取月干支纳音
     *
     * @return 月干支纳音（如：天上火）
     */
    public String getMonthGanZhiNaYin() {
        return DaLiuRenJiChuMap.NA_YIN.get(this.monthGanZhi);
    }

    /**
     * 获取日干支纳音
     *
     * @return 日干支纳音（如：天上火）
     */
    public String getDayGanZhiNaYin() {
        return DaLiuRenJiChuMap.NA_YIN.get(this.dayGanZhi);
    }

    /**
     * 获取时干支纳音
     *
     * @return 时干支纳音（如：天上火）
     */
    public String getHourGanZhiNaYin() {
        return DaLiuRenJiChuMap.NA_YIN.get(this.hourGanZhi);
    }


    /**
     * 获取年干支空亡
     *
     * @return 年干支空亡（如：子丑）
     */
    public String getYearGanZhiKongWang() {
        return DaLiuRenJiChuMap.KONG_WANG.get(this.yearGanZhi);
    }

    /**
     * 获取月干支空亡
     *
     * @return 月干支空亡（如：子丑）
     */
    public String getMonthGanZhiKongWang() {
        return DaLiuRenJiChuMap.KONG_WANG.get(this.monthGanZhi);
    }

    /**
     * 获取日干支空亡
     *
     * @return 日干支空亡（如：子丑）
     */
    public String getDayGanZhiKongWang() {
        return DaLiuRenJiChuMap.KONG_WANG.get(this.dayGanZhi);
    }

    /**
     * 获取时干支空亡
     *
     * @return 时干支空亡（如：子丑）
     */
    public String getHourGanZhiKongWang() {
        return DaLiuRenJiChuMap.KONG_WANG.get(this.hourGanZhi);
    }


    /**
     * 获取五行旺衰
     *
     * @return 五行旺衰（如：[木旺, 火相, 水休, 金囚, 土死]）
     */
    public List<String> getWuXingWangShuai() {
        return DaLiuRenJiChuMap.WU_XING_WANG_SHUAI.get(this.monthZhi);
    }


    /**
     * 获取上一节
     *
     * @return 上一节（如：大雪）
     */
    public String getPrevJie() {
        return getLunar().getPrevJie(this.daLiuRenJiChuSetting.getJieQiType() == 0).toString();
    }

    /**
     * 获取上一节日期
     *
     * @return 上一节日期（如：2023-12-07 17:32:44）
     */
    public String getPrevJieDateStr() {
        return getLunar().getPrevJie(this.daLiuRenJiChuSetting.getJieQiType() == 0).getSolar().toYmdHms();
    }

    /**
     * 获取距上一节天数
     *
     * @return 距上一节天数（如：24）
     */
    public int getPrevJieDay() {
        return Math.toIntExact(DateUtil.dateInterval(getPrevJieDateStr(), this.solar.toYmdHms()).get("days"));
    }

    /**
     * 获取下一节
     *
     * @return 下一节（如：小寒）
     */
    public String getNextJie() {
        return getLunar().getNextJie(this.daLiuRenJiChuSetting.getJieQiType() == 0).toString();
    }

    /**
     * 获取下一节日期
     *
     * @return 下一节日期（如：2024-01-06 04:49:08）
     */
    public String getNextJieDateStr() {
        return getLunar().getNextJie(this.daLiuRenJiChuSetting.getJieQiType() == 0).getSolar().toYmdHms();
    }

    /**
     * 获取距下一节天数
     *
     * @return 距下一节天数（如：5）
     */
    public int getNextJieDay() {
        return Math.toIntExact(DateUtil.dateInterval(this.solar.toYmdHms(), getNextJieDateStr()).get("days"));
    }

    /**
     * 获取出生节
     *
     * @return 出生节（如：大雪后24天6小时27分16秒、小寒前5天4小时49分8秒）
     */
    public String getChuShengJie() {

        Map<String, Long> prevMap = DateUtil.dateInterval(getPrevJieDateStr(), this.solar.toYmdHms()); // 计算上一节气与排盘日期的时间间隔
        Map<String, Long> nextMap = DateUtil.dateInterval(this.solar.toYmdHms(), getNextJieDateStr()); // 计算排盘日期与下一节气的时间间隔

        long prevDay = prevMap.get("days") > 0 ? prevMap.get("days") : -prevMap.get("days"); // 天
        long prevHours = prevMap.get("hours") > 0 ? prevMap.get("hours") : -prevMap.get("hours"); // 小时
        long prevMinutes = prevMap.get("minutes") > 0 ? prevMap.get("minutes") : -prevMap.get("minutes"); // 分
        long prevSeconds = prevMap.get("seconds") > 0 ? prevMap.get("seconds") : -prevMap.get("seconds"); // 秒

        long nextDay = nextMap.get("days") > 0 ? nextMap.get("days") : -nextMap.get("days"); // 天
        long nextHours = nextMap.get("hours") > 0 ? nextMap.get("hours") : -nextMap.get("hours"); // 小时
        long nextMinutes = nextMap.get("minutes") > 0 ? nextMap.get("minutes") : -nextMap.get("minutes"); // 分
        long nextSeconds = nextMap.get("seconds") > 0 ? nextMap.get("seconds") : -nextMap.get("seconds"); // 秒

        String prevStr = getPrevJie() + "后" + prevDay + "天" + prevHours + "小时" + prevMinutes + "分" + prevSeconds + "秒";
        String nextStr = getNextJie() + "前" + nextDay + "天" + nextHours + "小时" + nextMinutes + "分" + nextSeconds + "秒";

        return prevStr + "、" + nextStr;

    }


    /**
     * 获取上一气
     *
     * @return 上一气（如：冬至）
     */
    public String getPrevQi() {
        return getLunar().getPrevQi(this.daLiuRenJiChuSetting.getJieQiType() == 0).toString();
    }

    /**
     * 获取上一气日期
     *
     * @return 上一气日期（如：2023-12-22 11:27:09）
     */
    public String getPrevQiDateStr() {
        return getLunar().getPrevQi(this.daLiuRenJiChuSetting.getJieQiType() == 0).getSolar().toYmdHms();
    }

    /**
     * 获取距上一气天数
     *
     * @return 距上一气天数（如：9）
     */
    public int getPrevQiDay() {
        return Math.toIntExact(DateUtil.dateInterval(getPrevQiDateStr(), this.solar.toYmdHms()).get("days"));
    }

    /**
     * 获取下一气
     *
     * @return 下一气（如：大寒）
     */
    public String getNextQi() {
        return getLunar().getNextQi(this.daLiuRenJiChuSetting.getJieQiType() == 0).toString();
    }

    /**
     * 获取下一气日期
     *
     * @return 下一气日期（如：2024-01-20 22:07:08）
     */
    public String getNextQiDateStr() {
        return getLunar().getNextQi(this.daLiuRenJiChuSetting.getJieQiType() == 0).getSolar().toYmdHms();
    }

    /**
     * 获取距下一气天数
     *
     * @return 距下一节天数（如：19）
     */
    public int getNextQiDay() {
        return Math.toIntExact(DateUtil.dateInterval(this.solar.toYmdHms(), getNextQiDateStr()).get("days"));
    }

    /**
     * 获取出生气
     *
     * @return 出生气（如：冬至后9天12小时32分51秒、大寒前19天22小时7分8秒）
     */
    public String getChuShengQi() {

        Map<String, Long> prevMap = DateUtil.dateInterval(getPrevQiDateStr(), this.solar.toYmdHms()); // 计算上一节气与排盘日期的时间间隔
        Map<String, Long> nextMap = DateUtil.dateInterval(this.solar.toYmdHms(), getNextQiDateStr()); // 计算排盘日期与下一节气的时间间隔

        long prevDay = prevMap.get("days") > 0 ? prevMap.get("days") : -prevMap.get("days"); // 天
        long prevHours = prevMap.get("hours") > 0 ? prevMap.get("hours") : -prevMap.get("hours"); // 小时
        long prevMinutes = prevMap.get("minutes") > 0 ? prevMap.get("minutes") : -prevMap.get("minutes"); // 分
        long prevSeconds = prevMap.get("seconds") > 0 ? prevMap.get("seconds") : -prevMap.get("seconds"); // 秒

        long nextDay = nextMap.get("days") > 0 ? nextMap.get("days") : -nextMap.get("days"); // 天
        long nextHours = nextMap.get("hours") > 0 ? nextMap.get("hours") : -nextMap.get("hours"); // 小时
        long nextMinutes = nextMap.get("minutes") > 0 ? nextMap.get("minutes") : -nextMap.get("minutes"); // 分
        long nextSeconds = nextMap.get("seconds") > 0 ? nextMap.get("seconds") : -nextMap.get("seconds"); // 秒

        String prevStr = getPrevQi() + "后" + prevDay + "天" + prevHours + "小时" + prevMinutes + "分" + prevSeconds + "秒";
        String nextStr = getNextQi() + "前" + nextDay + "天" + nextHours + "小时" + nextMinutes + "分" + nextSeconds + "秒";

        return prevStr + "、" + nextStr;

    }


    /**
     * 获取月相
     *
     * @return 月相（如：朔）
     */
    public String getYueXiang() {
        return this.lunar.getYueXiang();
    }

    /**
     * 获取月将
     *
     * @return 月将（如：子）
     */
    public String getYueJiang() {
        return DaLiuRenJiChuMap.YUE_JIANG.get(getPrevQi() + getNextQi()).get(0);
    }

    /**
     * 获取月将神
     *
     * @return 月将神（如：神后）
     */
    public String getYueJiangShen() {
        return DaLiuRenJiChuMap.YUE_JIANG.get(getPrevQi() + getNextQi()).get(1);
    }


    /**
     * 获取三传取法
     *
     * @return 三传取法（如：贼克法）
     */
    public String getSanChuanQuFa() {
        return this.sanChuanQuFa;
    }

    /**
     * 获取天地盘类型
     *
     * @return 天地盘类型（如：伏吟盘）
     */
    public String getTianDiPanType() {
        return DaLiuRenJiChuMap.TIAN_DI_PAN_TYPE.get(getYueJiang() + this.hourZhi);
    }


    /**
     * 获取天干
     *
     * @return 天干（如：）
     */
    public List<String> getTianGan() {
        return this.tianGan;
    }

    /**
     * 获取地盘
     *
     * @return 地盘（如：[寅, 卯, 辰, 巳, 午, 未, 申, 酉, 戌, 亥, 子, 丑]）
     */
    public List<String> getDiPan() {
        return this.diPan;
    }

    /**
     * 获取天盘
     *
     * @return 天盘（如：[寅, 卯, 辰, 巳, 午, 未, 申, 酉, 戌, 亥, 子, 丑]）
     */
    public List<String> getTianPan() {
        return this.tianPan;
    }

    /**
     * 获取神盘
     *
     * @return 神盘（如：[太阴, 天后, 贵人, 螣蛇, 朱雀, 六合, 勾陈, 青龙, 天空, 白虎, 太常, 玄武]）
     */
    public List<String> getShenPan() {
        return this.shenPan;
    }


    /**
     * 获取四课
     *
     * @return 四课（如：[[午, 乙], [申, 午], [酉, 未], [亥, 酉]]）
     */
    public List<List<String>> getSiKe() {
        return this.siKe;
    }

    /**
     * 获取四课遁干
     *
     * @return 四课遁干（如：[[戊, ], [辛, 戊], [庚, 丁], [癸, 庚]]）
     */
    public List<List<String>> getSiKeDunGan() {

        // 处理第一课地盘天干
        String newYiKeDiPan = DaLiuRenJiChuMap.SHI_GAN_JI_GONG.get(this.siKe.get(0).get(1)); // 处理第一课，将地盘天干转换为寄宫地支
        List<List<String>> newSiKe = new ArrayList<>();
        newSiKe.add(0, Arrays.asList(this.siKe.get(0).get(0), newYiKeDiPan));
        newSiKe.add(1, this.siKe.get(1));
        newSiKe.add(2, this.siKe.get(2));
        newSiKe.add(3, this.siKe.get(3));

        List<List<String>> list = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            List<String> oneKe = new ArrayList<>(); // 每一课
            for (int j = 0; j < 2; j++) {
                for (int z = 0; z < 12; z++) {
                    if (newSiKe.get(i).get(j).equals(this.tianPan.get(z))) {
                        oneKe.add(this.tianGan.get(z));
                        break;
                    }
                }
            }
//            if (i == 0) oneKe.add(""); // 此处无需置空
            list.add(oneKe);
        }

        return list;

    }

    /**
     * 获取四课神将
     *
     * @return 四课神将（如：[[螣蛇, ], [太阴, 螣蛇], [天后, 朱雀], [太常, 天后]]）
     */
    public List<List<String>> getSiKeShenJiang() {

        // 处理第一课地盘天干
        String newYiKeDiPan = DaLiuRenJiChuMap.SHI_GAN_JI_GONG.get(this.siKe.get(0).get(1)); // 处理第一课，将地盘天干转换为寄宫地支
        List<List<String>> newSiKe = new ArrayList<>();
        newSiKe.add(0, Arrays.asList(this.siKe.get(0).get(0), newYiKeDiPan));
        newSiKe.add(1, this.siKe.get(1));
        newSiKe.add(2, this.siKe.get(2));
        newSiKe.add(3, this.siKe.get(3));

        List<List<String>> list = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            List<String> oneKe = new ArrayList<>(); // 每一课
            for (int j = 0; j < 2; j++) {
                for (int z = 0; z < 12; z++) {
                    if (newSiKe.get(i).get(j).equals(this.tianPan.get(z))) {
                        oneKe.add(this.shenPan.get(z));
                        break;
                    }
                }
            }
//            if (i == 0) oneKe.add(""); // 此处无需置空
            list.add(oneKe);
        }

        return list;

    }


    /**
     * 获取三传
     *
     * @return 三传（如：（如：[亥, 未, 丑]）
     */
    public List<String> getSanChuan() {
        return this.sanChuan;
    }

    /**
     * 获取三传遁干
     *
     * @return 三传遁干（如：[己, 乙, 辛]）
     */
    public List<String> getSanChuanDunGan() {

        List<String> list = new ArrayList<>();

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 12; j++) {
                if (this.sanChuan.get(i).equals(this.tianPan.get(j))) {
                    list.add(this.tianGan.get(j));
                    break;
                }
            }
        }

        return list;

    }

    /**
     * 获取三传神将
     *
     * @return 三传神将（如：[太阴, 天后, 贵人]）
     */
    public List<String> getSanChuanShenJiang() {

        List<String> list = new ArrayList<>();

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 12; j++) {
                if (this.sanChuan.get(i).equals(this.tianPan.get(j))) {
                    list.add(this.shenPan.get(j));
                    break;
                }
            }
        }

        return list;

    }

    /**
     * 获取三传六亲
     *
     * @return 三传六亲（如：[兄弟, 子孙, 子孙]）
     */
    public List<String> getSanChuanLiuQin() {

        Map<String, String> ganZhiShengKeLiuQinMap = DaLiuRenJiChuMap.GAN_ZHI_SHENG_KE_LIU_QIN; // 干支生克对应六亲（天干+地支为键）

        String chuChuanLiuQin = ganZhiShengKeLiuQinMap.get(this.dayGan + this.sanChuan.get(0)); // 初传六亲
        String zhongChuanLiuQin = ganZhiShengKeLiuQinMap.get(this.dayGan + this.sanChuan.get(1)); // 初传六亲
        String moChuanLiuQin = ganZhiShengKeLiuQinMap.get(this.dayGan + this.sanChuan.get(2)); // 初传六亲

        return Arrays.asList(chuChuanLiuQin, zhongChuanLiuQin, moChuanLiuQin);

    }

//-------------------------------------------------------------------------------------------------------------------------------

    /**
     * 计算十二贵人
     *
     * @param guiRenMap 贵人集合
     * @return 十二贵人（如：[太阴, 天后, 贵人, 螣蛇, 朱雀, 六合, 勾陈, 青龙, 天空, 白虎, 太常, 玄武]）
     */
    private List<String> computeShiErGuiRen(Map<String, String> guiRenMap) {

        List<String> guiRenShunXu = DaLiuRenJiChuMap.GUI_REN_SHUN_XU; // 贵人排列顺序

        String guiRen = guiRenMap.get(this.dayGan + this.hourZhi); // 贵人
        int guiRenGongWei = 1; // 贵人落入天盘的宫位
        for (int i = 0; i < 12; i++) {
            if (this.tianPan.get(i).equals(guiRen)) {
                guiRenGongWei = i;
                break;
            }
        }

        List<String> shiErGuiRen = CommonUtil.addCharToList(12, "");

        // 判断贵人排列方向（根据贵人落宫的地盘地支计算）
        if (DaLiuRenJiChuMap.GUI_REN_FANG_XIANG.get(this.diPan.get(guiRenGongWei)) == 0) {
            // 顺排
            for (int i = 0; i < 12; i++) {
                shiErGuiRen.set(guiRenGongWei, guiRenShunXu.get(i));
                guiRenGongWei++;
                if (guiRenGongWei > 11) guiRenGongWei = 0;
            }
        } else {
            // 逆排
            for (int i = 0; i < 12; i++) {
                shiErGuiRen.set(guiRenGongWei, guiRenShunXu.get(i));
                guiRenGongWei--;
                if (guiRenGongWei < 0) guiRenGongWei = 11;
            }
        }

        return shiErGuiRen;

    }

    /**
     * 计算三传（方法一：贼克法）
     *
     * @return 三传，依次为初传、中传、末传（如：[申, 亥, 寅]）
     */
    private List<String> computeSanChuanZeiKe() {

        // 前置条件一：若月将与时支相同，并且四课中的天盘与地盘相同，使用【方法八：伏吟法】计算三传
        boolean isYueJiangHourZhiTong = getYueJiang().equals(this.hourZhi); // 月将与时辰是否相同（true:相同。false:不同）
        boolean isYiKeTianDi = this.siKe.get(0).get(0).equals(DaLiuRenJiChuMap.SHI_GAN_JI_GONG.get(this.siKe.get(0).get(1))); // 第一课的天盘与地盘是否相同（true:相同。false:不同）
        boolean isErKeTianDi = this.siKe.get(1).get(0).equals(this.siKe.get(1).get(1)); // 第二课的天盘与地盘是否相同（true:相同。false:不同）
        boolean isSanKeTianDi = this.siKe.get(2).get(0).equals(this.siKe.get(2).get(1)); // 第三课的天盘与地盘是否相同（true:相同。false:不同）
        boolean isSiKeTianDi = this.siKe.get(3).get(0).equals(this.siKe.get(3).get(1)); // 第四课的天盘与地盘是否相同（true:相同。false:不同）
        if (isYueJiangHourZhiTong && isYiKeTianDi && isErKeTianDi && isSanKeTianDi && isSiKeTianDi) return computeSanChuanFuYin();

        // 前置条件二：若月将与时支相冲，并且天地盘中的天盘地支与地盘地支相冲，使用【方法九：反吟法】计算三传
        String yueJiangHourZhiChong = DaLiuRenJiChuMap.DI_ZHI_XIANG_CHONG.get(this.hourZhi);
        boolean isTianDiPanChong = "申".equals(tianPan.get(0)); // 此处只需判断一个宫位即可
        if (null != yueJiangHourZhiChong && isTianDiPanChong) return computeSanChuanFanYin();

        boolean isYiKe = false; // 第一课是否符合条件（true:是。false:否）

        // 1、判断是否符合下克上
        List<String> xiaKeShang = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            List<String> data = DaLiuRenJiChuMap.ZE_KE_XIA_KE_SHANG.get(this.siKe.get(i).get(0) + this.siKe.get(i).get(1));
            if (null != data) {
                xiaKeShang.add(data.get(0));
                if (i == 0) isYiKe = true;
            }
        }
        if (xiaKeShang.size() == 1) {
            // 存在一个
            this.sanChuanQuFa = "贼克法"; // 三传取法
            String chuChuan = xiaKeShang.get(0); // 初传
            return addSanChuan(chuChuan); // 添加三传
        } else if (xiaKeShang.size() > 1) {
            // 存在多个，使用【方法二：比用法】计算三传
            return computeSanChuanBiYong(xiaKeShang, isYiKe);
        }

        // 2、判断是否符合上克下
        List<String> shangKeXia = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            List<String> data = DaLiuRenJiChuMap.ZE_KE_SHANG_KE_XIA.get(this.siKe.get(i).get(0) + this.siKe.get(i).get(1));
            if (null != data) {
                shangKeXia.add(data.get(0));
                if (i == 0) isYiKe = true;
            }
        }
        if (shangKeXia.size() == 1) {
            // 存在一个
            this.sanChuanQuFa = "贼克法"; // 三传取法
            String chuChuan = shangKeXia.get(0); // 初传
            return addSanChuan(chuChuan); // 添加三传
        } else if (shangKeXia.size() > 1) {
            // 存在多个，使用【方法二：比用法】计算三传
            return computeSanChuanBiYong(shangKeXia, isYiKe);
        }

        // 3、若执行到此处，则说明没有[下克上]或[上克下]数据，使用【方法四：遥克法】计算三传
        return computeSanChuanYaoKe();

    }

    /**
     * 计算三传（方法二：比用法）
     *
     * @param chuChuanList 初传集合
     * @param isYiKe       第一课是否符合条件（true:是。false:否）
     * @return 三传，依次为初传、中传、末传（如：[申, 亥, 寅]）
     */
    private List<String> computeSanChuanBiYong(List<String> chuChuanList, boolean isYiKe) {

        // 1、统计初传集合的阴阳属性
        List<String> chuChuanYinYangList = new ArrayList<>();
        for (String value : chuChuanList) {
            chuChuanYinYangList.add(DaLiuRenJiChuMap.DI_ZHI_YIN_YANG.get(value));
        }

        // 2、获取日干阴阳
        String dayGanYinYang = DaLiuRenJiChuMap.TIAN_GAN_YIN_YANG.get(this.dayGan);
        Map<Object, List<Integer>> duplicateIndexList = CommonUtil.getDuplicateIndexList(chuChuanYinYangList, false);
        List<Integer> yinYang = duplicateIndexList.get(dayGanYinYang); // 和日干阴阳相同的数据
        if (null != yinYang && yinYang.size() == 1) {
            String chuChuan = ""; // 初传
            for (String value : chuChuanList) {
                if (dayGanYinYang.equals(DaLiuRenJiChuMap.DI_ZHI_YIN_YANG.get(value))) {
                    chuChuan = value;
                    break;
                }
            }
            this.sanChuanQuFa = "比用法"; // 三传取法
            return addSanChuan(chuChuan); // 添加三传
        }

        // 3、判断计算方法
        if (CommonUtil.distinctList(chuChuanYinYangList)) {
            // 3.1、判断四课是否为二课，若为二课则使用【方法七：八专法】计算三传（指四课中没有重复出现的课）
            String newYiKe = this.siKe.get(0).get(0) + DaLiuRenJiChuMap.SHI_GAN_JI_GONG.get(this.siKe.get(0).get(1)); // 处理第一课，将地盘天干转换为寄宫地支
            String newErKe = this.siKe.get(1).get(0) + this.siKe.get(1).get(1); // 处理第二课
            String newSanKe = this.siKe.get(2).get(0) + this.siKe.get(2).get(1); // 处理第三课
            String newSiKe = this.siKe.get(3).get(0) + this.siKe.get(3).get(1); // 处理第四课
            List<String> list = Arrays.asList(newYiKe, newErKe, newSanKe, newSiKe);
            Map<Object, List<Integer>> duplicateIndexListKe = CommonUtil.getDuplicateIndexList(list, false);
            if (duplicateIndexListKe.size() == 2) return computeSanChuanBaZhuan();
            // 3.2、有重复数据，使用【方法三：涉害法】计算三传
            List<Integer> yin = duplicateIndexList.get("阴"); // 五行属性为[阴]的数据索引
            List<Integer> yang = duplicateIndexList.get("阳"); // 五行属性为[阳]的数据索引
            // 3.3、只有 [阴]有重复数据 或 [阳]有重复数据
            List<String> newChuChuanList = new ArrayList<>();
            if (null != yin) {
                // 3.3.1、[阴]有重复数据，则返回全部[阴]属性数据
                for (Integer value : yin) {
                    newChuChuanList.add(chuChuanList.get(value));
                }
            } else {
                // 3.3.2、[阳]有重复数据，则返回全部[阳]属性数据
                for (Integer value : yang) {
                    newChuChuanList.add(chuChuanList.get(value));
                }
            }
            chuChuanList = newChuChuanList;
            return computeSanChuanSheHai(chuChuanList, isYiKe);
        }

        return null;

    }

    /**
     * 计算三传（方法三：涉害法）
     *
     * @param chuChuanList 初传集合
     * @param isYiKe       第一课是否符合条件（true:是。false:否）
     * @return 三传，依次为初传、中传、末传（如：[申, 亥, 寅]）
     */
    private List<String> computeSanChuanSheHai(List<String> chuChuanList, boolean isYiKe) {

        /*
            涉害法又有两种计算方式：
                1、孟仲涉害法（← 采用）
                2、逐一对比法
         */

        String chuChuan = "";  // 初传

        // 1、若第一课是否符合条件，则将地盘天干转为地支
        if (isYiKe && chuChuanList.size() >= 1) {
            chuChuanList.set(0, DaLiuRenJiChuMap.SHI_GAN_JI_GONG.get(this.siKe.get(0).get(1)));
        }

        // 2、依次判断孟仲季
        for (String value : chuChuanList) {
            if ("孟".equals(DaLiuRenJiChuMap.DI_ZHI_MENG_ZHONG_JI.get(value))) chuChuan = value;
        }
        if ("".equals(chuChuan)) {
            for (String value : chuChuanList) {
                if ("仲".equals(DaLiuRenJiChuMap.DI_ZHI_MENG_ZHONG_JI.get(value))) chuChuan = value;
            }
        }
        if ("".equals(chuChuan)) {
            for (String value : chuChuanList) {
                if ("季".equals(DaLiuRenJiChuMap.DI_ZHI_MENG_ZHONG_JI.get(value))) chuChuan = value;
            }
        }

        this.sanChuanQuFa = "涉害法"; // 三传取法

        return addSanChuan(chuChuan); // 添加三传

    }

    /**
     * 计算三传（方法四：遥克法）
     *
     * @return 三传，依次为初传、中传、末传（如：[申, 亥, 寅]）
     */
    private List<String> computeSanChuanYaoKe() {

        /*
            四课无克号为遥，日与神兮递互招（看日干与四课天盘之间的相克关系）
            先取神遥克其日，如无方取日来遥（先看四课中天盘来克日干的，若没有再看日干去克别的天盘的）
            复有两神来克日，或有日去克两神（若有两个来克日干的，或者日干同时克两个的）
            当择日干比者用，阳日用阳阴用阴（使用比用法，对比阴阳属性，阳日用阳，阴日用阴）
         */

        boolean isYiKe = false; // 第一课是否符合条件（true:是。false:否）

        // 1、判断四课天盘地支是否存在克日干的关系
        Map<String, String> zhiGanXiangKeMap = DaLiuRenJiChuMap.ZHI_GAN_XIANG_KE; // 干支相克（地支+天干为键）
        String zhiGan1 = zhiGanXiangKeMap.get(this.siKe.get(0).get(0) + this.dayGan); // 第一课天盘地支是否克日干
        String zhiGan2 = zhiGanXiangKeMap.get(this.siKe.get(1).get(0) + this.dayGan); // 第二课天盘地支是否克日干
        String zhiGan3 = zhiGanXiangKeMap.get(this.siKe.get(2).get(0) + this.dayGan); // 第三课天盘地支是否克日干
        String zhiGan4 = zhiGanXiangKeMap.get(this.siKe.get(3).get(0) + this.dayGan); // 第四课天盘地支是否克日干
        List<String> zhiGanList = new ArrayList<>();
        if (null != zhiGan1) {
            zhiGanList.add(this.siKe.get(0).get(0));
            isYiKe = true;
        }
        if (null != zhiGan2) {
            zhiGanList.add(this.siKe.get(1).get(0));
        }
        if (null != zhiGan3) {
            zhiGanList.add(this.siKe.get(2).get(0));
        }
        if (null != zhiGan4) {
            zhiGanList.add(this.siKe.get(3).get(0));
        }
        if (zhiGanList.size() == 1) {
            // 存在一个相克关系
            this.sanChuanQuFa = "遥克法"; // 三传取法
            String chuChuan = zhiGanList.get(0); // 初传
            return addSanChuan(chuChuan); // 添加三传
        } else if (zhiGanList.size() > 1) {
            // 存在多个相克关系，使用【方法二：比用法】计算三传
            return computeSanChuanBiYong(zhiGanList, isYiKe);
        }

        // 2、判断日干是否存在克四课天盘地支的关系
        Map<String, String> ganZhiXiangKeMap = DaLiuRenJiChuMap.GAN_ZHI_XIANG_KE; // 干支相克（天干+地支为键）
        String ganZhi1 = ganZhiXiangKeMap.get(this.dayGan + this.siKe.get(0).get(0)); // 日干是否克第一课天盘地支
        String ganZhi2 = ganZhiXiangKeMap.get(this.dayGan + this.siKe.get(1).get(0)); // 日干是否克第二课天盘地支
        String ganZhi3 = ganZhiXiangKeMap.get(this.dayGan + this.siKe.get(2).get(0)); // 日干是否克第三课天盘地支
        String ganZhi4 = ganZhiXiangKeMap.get(this.dayGan + this.siKe.get(3).get(0)); // 日干是否克第四课天盘地支
        List<String> ganZhiList = new ArrayList<>();
        if (null != ganZhi1) {
            ganZhiList.add(this.siKe.get(0).get(0));
            isYiKe = true;
        }
        if (null != ganZhi2) {
            ganZhiList.add(this.siKe.get(1).get(0));
        }
        if (null != ganZhi3) {
            ganZhiList.add(this.siKe.get(2).get(0));
        }
        if (null != ganZhi4) {
            ganZhiList.add(this.siKe.get(3).get(0));
        }
        if (ganZhiList.size() == 1) {
            // 存在一个相克关系
            this.sanChuanQuFa = "遥克法"; // 三传取法
            String chuChuan = ganZhiList.get(0); // 初传
            return addSanChuan(chuChuan); // 添加三传
        } else if (ganZhiList.size() > 1) {
            // 存在多个相克关系，使用【方法二：比用法】计算三传
            return computeSanChuanBiYong(ganZhiList, isYiKe);
        }

        // 3、若执行到此处，则说明没有[下克上]或[上克下]或[天盘地支克日干]或[日干克天盘地支]数据，使用【方法五：昴星法】计算三传
        return computeSanChuanMaoXing();

    }

    /**
     * 计算三传（方法五：昴星法）
     *
     * @return 三传，依次为初传、中传、末传（如：[申, 亥, 寅]）
     */
    private List<String> computeSanChuanMaoXing() {

        /*
            无遥当向昴星寻（没有遥克，应该转用昴星法）
            阳仰阴俯酉位中（阳日取初传，仰视，是地盘酉的上神，看酉的上面是谁；阴日是俯视，看天盘酉的下神是谁）
            刚日先辰而后日（指阳日，辰指的是日支，日指的是日干。这句话说的是取中传和末传的取法，阳日的中传在日支的上面（不是地支，而是地支的上面是谁）。末传，是在日干的上面）
            柔日先干而后辰（指阴日，阴日的中、末传取法和阳日顺序刚好相反。中传是在日干的上面，末传在日支的上面）
         */

        // 1、判断四课是否全备（指四课中没有重复出现的课）
        String newYiKe = this.siKe.get(0).get(0) + DaLiuRenJiChuMap.SHI_GAN_JI_GONG.get(this.siKe.get(0).get(1)); // 处理第一课，将地盘天干转换为寄宫地支
        String newErKe = this.siKe.get(1).get(0) + this.siKe.get(1).get(1); // 处理第二课
        String newSanKe = this.siKe.get(2).get(0) + this.siKe.get(2).get(1); // 处理第三课
        String newSiKe = this.siKe.get(3).get(0) + this.siKe.get(3).get(1); // 处理第四课
        List<String> list = Arrays.asList(newYiKe, newErKe, newSanKe, newSiKe);
        Map<Object, List<Integer>> duplicateIndexList = CommonUtil.getDuplicateIndexList(list, false);
        if (duplicateIndexList.size() == 4) {
            String dayGanYinYang = DaLiuRenJiChuMap.TIAN_GAN_YIN_YANG.get(this.dayGan); // 日干阴阳
            if ("阳".equals(dayGanYinYang)) {
                // 获取地盘地支[酉]宫位（索引固定为7）的天盘地支【从天地盘获取】
                this.sanChuanQuFa = "昴星法"; // 三传取法
                String chuChuan = this.tianPan.get(7); // 初传
                return addSanChuan(chuChuan, true); // 添加三传
            } else {
                // 获取天盘地支[酉]宫位的地盘地支【从天地盘获取】
                this.sanChuanQuFa = "昴星法"; // 三传取法
                for (int i = 0; i < 12; i++) {
                    if ("酉".equals(this.tianPan.get(i))) {
                        String chuChuan = this.diPan.get(i); // 初传
                        return addSanChuan(chuChuan, false); // 添加三传
                    }
                }
            }
        } else if (duplicateIndexList.size() == 3) {
            // 三课，使用【方法六：别责法】计算三传
            return computeSanChuanBieZe();
        } else if (duplicateIndexList.size() == 2) {
            // 二课，使用【方法七：八专法】计算三传
            return computeSanChuanBaZhuan();
        }

        return null;

    }

    /**
     * 计算三传（方法六：别责法）
     *
     * @return 三传，依次为初传、中传、末传（如：[申, 亥, 寅]）
     */
    private List<String> computeSanChuanBieZe() {

        /*
            四课不全三课备，无遥无克别责例（四课不备，且有三课，无遥克、直接的克，则用别责法）
            刚日干合上头神，柔日支前三合取（阳日，看与日干相合的天干的上面是谁，初传就是谁。阴日，是看日支的三合，初传为三合的前一个地支）
            皆以天上作初传，阴阳中末干中寄（初传是看上神，不管阴日还是阳日，它的中传、末传都是日干的上神）
         */

        this.sanChuanQuFa = "别责法"; // 三传取法

        // 1、根据日干阴阳区分计算方法
        String dayGanYinYang = DaLiuRenJiChuMap.TIAN_GAN_YIN_YANG.get(this.dayGan); // 日干阴阳
        if ("阳".equals(dayGanYinYang)) {
            // 1.1、日干为阳
            String diZhi = DaLiuRenJiChuMap.SHI_GAN_JI_GONG.get(DaLiuRenJiChuMap.TIAN_GAN_XIANG_HE.get(this.dayGan)); // 将相合的日干转换为寄宫地支
            for (int i = 0; i < 12; i++) {
                if (diZhi.equals(this.diPan.get(i))) {
                    String chuChuan = this.tianPan.get(i);
                    return Arrays.asList(chuChuan, this.siKe.get(0).get(0), this.siKe.get(0).get(0));
                }
            }
        } else {
            // 1.2、日干为阴
            String chuChuan = DaLiuRenJiChuMap.DI_ZHI_SAN_HE.get(this.dayZhi);
            return Arrays.asList(chuChuan, this.siKe.get(0).get(0), this.siKe.get(0).get(0));
        }

        return null;

    }

    /**
     * 计算三传（方法七：八专法）
     *
     * @return 三传，依次为初传、中传、末传（如：[申, 亥, 寅]）
     */
    private List<String> computeSanChuanBaZhuan() {

        /*
            两课无克号八专，阳日日阳顺行三（只有两课，且没有克，使用八专法。阳日日干上神（干阳）在天地盘中，顺时针数三位为初传）
            阴日辰阴逆三位，中末总归日上眠（阴日取初传，则从支阴在天地盘中，逆时针数三位初传。中传、末传都是在四课中，看日干的上神）
         */

        this.sanChuanQuFa = "八专法"; // 三传取法

        // 1、根据日干阴阳区分计算方法
        String dayGanYinYang = DaLiuRenJiChuMap.TIAN_GAN_YIN_YANG.get(this.dayGan); // 日干阴阳
        if ("阳".equals(dayGanYinYang)) {
            // 1.1、获取第一课天盘地支，顺时针向后第三个地支即初传
            String diZhi = this.siKe.get(0).get(0);
            for (int i = 0; i < 12; i++) {
                if (diZhi.equals(this.tianPan.get(i))) {
                    int index = i + 2;
                    if (index > 11) index -= 12;
                    String chuChuan = this.tianPan.get(index);
                    return Arrays.asList(chuChuan, this.siKe.get(0).get(0), this.siKe.get(0).get(0));
                }
            }
        } else {
            // 1.2、日干为阴
            String diZhi = this.siKe.get(3).get(0);
            for (int i = 0; i < 12; i++) {
                if (diZhi.equals(this.tianPan.get(i))) {
                    int index = i - 2;
                    if (index < 0) index += 12;
                    String chuChuan = this.tianPan.get(index);
                    return Arrays.asList(chuChuan, this.siKe.get(0).get(0), this.siKe.get(0).get(0));
                }
            }
        }

        return null;

    }

    /**
     * 计算三传（方法八：伏吟法）
     *
     * @return 三传，依次为初传、中传、末传（如：[申, 亥, 寅]）
     */
    private List<String> computeSanChuanFuYin() {

        /*
            伏吟有克还为用，无克刚干柔取辰
            迤逦刑之做中末，从兹玉历识其真
            若也自刑为发用，次传颠倒日辰并
            次传更复自刑者，冲取末传不论刑
         */

        this.sanChuanQuFa = "伏吟法"; // 三传取法

        String chuChuan; // 初传
        String zhongChuan; // 中传
        String moChuan; // 末传

        // 1、判断是否符合[下克上]或[上克下]
        List<String> xiaKeShang = DaLiuRenJiChuMap.ZE_KE_XIA_KE_SHANG.get(this.siKe.get(0).get(0) + this.siKe.get(0).get(1));
        List<String> shangKeXia = DaLiuRenJiChuMap.ZE_KE_SHANG_KE_XIA.get(this.siKe.get(0).get(0) + this.siKe.get(0).get(1));
        String dayGanYinYang = DaLiuRenJiChuMap.TIAN_GAN_YIN_YANG.get(this.dayGan); // 日干阴阳
        if (null != xiaKeShang || null != shangKeXia) {
            if (null != xiaKeShang) {
                chuChuan = xiaKeShang.get(0); // 初传
            } else {
                chuChuan = shangKeXia.get(0); // 初传
            }
        } else {
            chuChuan = "阳".equals(dayGanYinYang) ? this.siKe.get(0).get(0) : this.siKe.get(2).get(0); // 初传
        }
        zhongChuan = DaLiuRenJiChuMap.DI_ZHI_XIANG_XING.get(chuChuan); // 中传
        if (zhongChuan.equals(chuChuan)) {
            zhongChuan = "阳".equals(dayGanYinYang) ? this.siKe.get(2).get(0) : this.siKe.get(0).get(0); // 中传
        }
        moChuan = DaLiuRenJiChuMap.DI_ZHI_XIANG_XING.get(zhongChuan); // 末传
        if (moChuan.equals(zhongChuan)) {
            moChuan = DaLiuRenJiChuMap.DI_ZHI_XIANG_CHONG.get(zhongChuan);
        } else {
            moChuan = DaLiuRenJiChuMap.DI_ZHI_XIANG_XING.get(zhongChuan);
        }

        return Arrays.asList(chuChuan, zhongChuan, moChuan);

    }

    /**
     * 计算三传（方法九：返吟法）
     *
     * @return 三传，依次为初传、中传、末传（如：[申, 亥, 寅]）
     */
    private List<String> computeSanChuanFanYin() {

        boolean isYiKe = false; // 第一课是否符合条件（true:是。false:否）

        // 1、判断是否符合下克上
        List<String> xiaKeShang = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            List<String> data = DaLiuRenJiChuMap.ZE_KE_XIA_KE_SHANG.get(this.siKe.get(i).get(0) + this.siKe.get(i).get(1));
            if (null != data) {
                xiaKeShang.add(data.get(0));
                if (i == 0) isYiKe = true;
            }
        }
        if (xiaKeShang.size() == 1) {
            // 存在一个
            this.sanChuanQuFa = "返吟法"; // 三传取法
            String chuChuan = xiaKeShang.get(0); // 初传
            return addSanChuan(chuChuan); // 添加三传
        } else if (xiaKeShang.size() > 1) {
            // 存在多个，使用【方法二：比用法】计算三传
            return computeSanChuanBiYong(xiaKeShang, isYiKe);
        }

        // 2、判断是否符合上克下
        List<String> shangKeXia = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            List<String> data = DaLiuRenJiChuMap.ZE_KE_SHANG_KE_XIA.get(this.siKe.get(i).get(0) + this.siKe.get(i).get(1));
            if (null != data) {
                shangKeXia.add(data.get(0));
                if (i == 0) isYiKe = true;
            }
        }
        if (shangKeXia.size() == 1) {
            // 存在一个
            this.sanChuanQuFa = "返吟法"; // 三传取法
            String chuChuan = shangKeXia.get(0); // 初传
            return addSanChuan(chuChuan); // 添加三传
        } else if (shangKeXia.size() > 1) {
            // 存在多个，使用【方法二：比用法】计算三传
            return computeSanChuanBiYong(shangKeXia, isYiKe);
        }

        // 3、若执行到此处，则说明没有[下克上]或[上克下]数据
        this.sanChuanQuFa = "返吟法"; // 三传取法
        String chuChuan = DaLiuRenJiChuMap.YI_MA.get(this.dayZhi); // 初传
        String zhongChuan = this.siKe.get(2).get(0); // 中传
        String moChuan = this.siKe.get(0).get(0); // 末传
        return Arrays.asList(chuChuan, zhongChuan, moChuan);

    }

    /**
     * 添加三传（适用于：贼克法、比用法、涉害法、遥克法）
     *
     * @param chuChuan 初传
     * @return 三传，依次为初传、中传、末传（如：[申, 亥, 寅]）
     */
    protected List<String> addSanChuan(String chuChuan) {

        String zhongChuan = ""; // 中传
        String moChuan = ""; // 末传

        for (int i = 0; i < 12; i++) {
            if (chuChuan.equals(this.diPan.get(i))) zhongChuan = this.tianPan.get(i);
        }
        for (int i = 0; i < 12; i++) {
            if (zhongChuan.equals(this.diPan.get(i))) moChuan = this.tianPan.get(i);
        }

        return Arrays.asList(chuChuan, zhongChuan, moChuan);

    }

    /**
     * 添加三传（适用于：昴星法）
     *
     * @param chuChuan  初传
     * @param isYangDay 是否为阳日（true:是。false:否）
     * @return 三传，依次为初传、中传、末传（如：[申, 亥, 寅]）
     */
    protected List<String> addSanChuan(String chuChuan, boolean isYangDay) {

        String zhongChuan = ""; // 中传
        String moChuan = ""; // 末传

        if (isYangDay) {
            for (int i = 0; i < 4; i++) {
                if (this.dayZhi.equals(this.siKe.get(i).get(1))) zhongChuan = this.siKe.get(i).get(0); // 中传
            }
            if ("".equals(zhongChuan)) {
                for (int i = 0; i < 12; i++) {
                    if (dayZhi.equals(this.tianPan.get(i))) zhongChuan = this.tianPan.get(i); // 中传
                }
            }
            moChuan = this.siKe.get(0).get(0); // 末传
        } else {
            zhongChuan = this.siKe.get(0).get(0); // 中传
            for (int i = 0; i < 4; i++) {
                if (this.dayZhi.equals(this.siKe.get(i).get(1))) moChuan = this.siKe.get(i).get(0); // 末传
            }
            if ("".equals(moChuan)) {
                for (int i = 0; i < 12; i++) {
                    if (dayZhi.equals(this.tianPan.get(i))) moChuan = this.tianPan.get(i); // 末传
                }
            }
        }

        return Arrays.asList(chuChuan, zhongChuan, moChuan);

    }

//-------------------------------------------------------------------------------------------------------------------------------

    /**
     * 初始化地盘
     */
    protected void initializeDiPan() {
        this.diPan = DaLiuRenJiChuMap.SHI_ER_GONG_DI_ZHI;
    }

    /**
     * 初始化天盘
     */
    protected void initializeTianPan() {

        /* 口诀：月将加占时，顺行十二辰（将月将加于地盘的时支上，依次顺排十二天将，即天盘） */

        // 1、获取月将落入时支（地盘地支）的宫位
        int yueJiangGongWei = 0; // 月将落入的宫位
        for (int i = 0; i < 12; i++) {
            if (this.diPan.get(i).equals(this.hourZhi)) {
                yueJiangGongWei = i;
                break;
            }
        }

        // 2、从月将落入的宫位开始顺时针添加天盘
        List<String> diZhiShunPai = DaLiuRenJiChuMap.DI_ZHI_SHUN_PAI.get(getYueJiang());
        List<String> tianPan = CommonUtil.addCharToList(12, "");
        for (int i = 0; i < 12; i++) {
            tianPan.set(yueJiangGongWei, diZhiShunPai.get(i));
            yueJiangGongWei++;
            if (yueJiangGongWei > 11) yueJiangGongWei = 0;
        }

        this.tianPan = tianPan;

    }

    /**
     * 初始化天干
     */
    protected void initializeTianGan() {

        // 获取日干支空亡的天干落在天地盘中天盘的哪个宫位
        final String daGanZhiKongWang = this.getDayGanZhiKongWang().substring(1, 2);
        for (int i = 0; i < 12; i++) {
            if (this.tianPan.get(i).equals(daGanZhiKongWang)) {
                this.tianGan = DaLiuRenJiChuMap.DUN_GAN_SHUN.get(i + 1);
            }
        }

    }

    /**
     * 初始化神盘
     */
    protected void initializeShenPan() {

        // 判断贵人计算类型
        if (this.daLiuRenJiChuSetting.getGuiRenType() == 0) {
            // 1、自动切换
            this.shenPan = computeShiErGuiRen(DaLiuRenJiChuMap.ZHOU_YE_GUI_REN);
        } else if (this.daLiuRenJiChuSetting.getGuiRenType() == 1) {
            // 2、昼贵
            this.shenPan = computeShiErGuiRen(DaLiuRenJiChuMap.ZHOU_GUI_REN);
        } else if (this.daLiuRenJiChuSetting.getGuiRenType() == 2) {
            // 3、夜贵
            this.shenPan = computeShiErGuiRen(DaLiuRenJiChuMap.YE_GUI_REN);
        }

    }

    /**
     * 初始化四课
     */
    protected void initializeSiKe() {

        List<List<String>> siKe = new ArrayList<>();

        // 1、计算第一课
        String jiGongDiZhi = DaLiuRenJiChuMap.SHI_GAN_JI_GONG.get(getDayGan()); // 获取十干寄宫地盘地支
        String ganYang = ""; // 干阳
        for (int i = 0; i < 12; i++) {
            if (this.diPan.get(i).equals(jiGongDiZhi)) {
                ganYang = this.tianPan.get(i);
            }
        }
        siKe.add(Arrays.asList(ganYang, getDayGan()));

        // 2、计算第二课
        String ganYin = ""; // 干阴
        for (int i = 0; i < 12; i++) {
            if (this.diPan.get(i).equals(ganYang)) {
                ganYin = this.tianPan.get(i);
            }
        }
        siKe.add(Arrays.asList(ganYin, ganYang));

        // 3、计算第三课
        String zhiYang = ""; // 支阳
        for (int i = 0; i < 12; i++) {
            if (this.diPan.get(i).equals(getDayZhi())) {
                zhiYang = this.tianPan.get(i);
            }
        }
        siKe.add(Arrays.asList(zhiYang, getDayZhi()));

        // 4、计算第四课
        String zhiYin = ""; // 支阴
        for (int i = 0; i < 12; i++) {
            if (this.diPan.get(i).equals(zhiYang)) {
                zhiYin = this.tianPan.get(i);
            }
        }
        siKe.add(Arrays.asList(zhiYin, zhiYang));

        this.siKe = siKe;

    }

    /**
     * 初始化三传
     */
    protected void initializeSanChuan() {

        /*
            三传的九种算法：

                1、贼克法：取课先从下克呼，若无下克上为初。初传本位明中次，中上因加是末居。
                2、比用法：下克或二三四侵，不然上克亦同论。常将天日相比取，阳日用阳阴用阴。
                3、涉害法：涉害由来是本家，路途多克最堪夸。孟深仲浅季无取，复向柔辰刚日查。
                4、遥克法：四课无克号为遥，日与天神互递招。先取神遥克其日，后则可与日相交。
                5、昂星法：无遥始向昂星寻，阳仰阴俯酉中神。刚日先辰而后日，柔日先日而后辰。
                6、别责法：三课无克别责名，刚日先传干上神。柔日支前三合取，中末都来干上神。
                7、八专法：两课无克号八专，阳日阳神顺行三。阴日阴神逆三数，中末都来干上眠。
                8、伏吟法：六甲伏吟寅已申，六丙六戌已申寅。六乙便寻辰戌未，六庚申寅已三刑。六已丁中临亥酉，辰先日后末取刑。丑卯已未便无克，辰刑冲处三传神。六癸便为丑戌未，壬辰壬午亥已申。更有四壬别立法，日先辰次末取刑。以上各干伏吟法，刚日柔辰中末分。
                9、返吟法：返吟有克克初生，理取先冲而后刑。次传如在自刑上，须求破冲是原因。
         */

        // 计算三传，从【方法一：贼克法】依次向下判断
        this.sanChuan = computeSanChuanZeiKe();
        if (null == this.sanChuan) this.sanChuan = Arrays.asList("申", "寅", "亥"); // TODO：待修复

    }

    @Override
    public String toString() {

        return "公历:" + getSolarStr() + "   " +
                "农历:" + getLunarStr() + "   " +
                "星期:" + getXingQi() + "   " +
                "季节:" + getJiJie() + "   " +
                "生肖:" + getShengXiao() + "   " +
                "星座:" + getXingZuo() + "   " +
                "月相:" + getYueXiang() + "   " +
                "月将:" + getYueJiang() + "   " +
                "月将神:" + getYueJiangShen() + "   " +
                "五不遇时:" + getWuBuYuShi() + "   " +
                "三传类型:" + getSanChuanQuFa() + "   " +
                "天地盘类型:" + getTianDiPanType() + "   " +
                "地盘:" + getDiPan() + "   " +
                "天盘:" + getTianPan() + "   " +
                "神盘:" + getShenPan();

    }


}
